#include "Miner.h"

#include <iostream>
#include ".\state\GoHomeAndSleepTilRested.h"
#include ".\state\MinerGlobalState.h"
//#include ".\state\MovingState.h"

#define G 9.80665f

#define METER_TO_DXUNIT(val) (val * 10.0f)
#define DXUNIT_TO_METER(val) (val * 0.1f)


Miner::Miner(int id)
: BaseGameEntity(id)
, m_Location(Shack)
, m_fGoldCarried(0.0f)
, m_iMoneyInBank(0)
, m_fThirst(0.0f)
, m_iTargetAmount(0)
, m_fTopSpeed(20.0f)
, m_fAcceleration(10.0f)
, m_fLastUpdateTime(0.0f)
{
	AddNameOfEntity(ID(), _T("Miner Bob"));

	m_pStateMachine = new StateMachine<Miner>(this);
	m_pStateMachine->SetCurrentState(GoHomeAndSleepTilRested::Instance());
	m_pStateMachine->SetGlobalState(MinerGlobalState::Instance());

	ZeroMemory(&m_vecSpeed, sizeof(m_vecSpeed));
	m_Position = pWorld->GetPoint(m_Location);
	m_NextPosition = m_Position;
	m_vecPos = pWorld->GetAreaVector(m_Position.x, m_Position.y);
	m_bInMoving = false;

	m_fJumpSpeed = sqrtf(m_fTopSpeed / 3.0f);
	m_fHangTime = m_fJumpSpeed * 2.0f / G;
	m_fBeginJumpTime = 0.0f;

	memset(&m_TargetPosition, 0xFF, sizeof(m_TargetPosition));
}

Miner::~Miner(void)
{
	delete m_pStateMachine;
}

void Miner::Update(float fTime)
{
	m_pStateMachine->Update(fTime);
	m_fLastUpdateTime = fTime;
}

//bool Miner::ChangeLocation(Locations dest, float fTime)
//{
//	m_Distination = dest;
//	return m_pStateMachine->ChangeState(MovingState::Instance(), fTime);
//}

bool Miner::Walking(float fTime)
{
	// Begin:
	// // Search route
	if (m_TargetPosition.x == ~0U, m_TargetPosition.y == ~0U)
	{
		m_TargetPosition = pWorld->GetPoint(m_Distination);
		pWorld->FindRoute(m_Position, m_TargetPosition, &m_listRoute);
	}

	D3DXVECTOR3 vecTo;

	// Moving:
	// The next confirmation
	while (m_Position == m_NextPosition)
	{
		if (m_listRoute.size())
		{
			m_NextPosition = m_listRoute.front();
			m_listRoute.pop_front();
			m_bInMoving = true;
		}
		else break;
	}

	//if (m_NextPosition)

	// Calculate the direction of the next.
	float fElapsedTime = fTime - m_fBeginJumpTime;

	// Begin Jumping
	if (fElapsedTime >= m_fHangTime)
	{
		m_fBeginJumpTime = fTime;
		fElapsedTime = 0.0f;

		m_vecJumpPos = m_vecPos + METER_TO_DXUNIT(m_vecSpeed * fElapsedTime);
		m_vecPos = m_vecJumpPos;
		m_vecPos.y = 5.0f;

		vecTo = pWorld->GetAreaVector(m_NextPosition.x, m_NextPosition.y);
		vecTo.y = 5.0f;
		D3DXVECTOR3 vecDirection = vecTo - m_vecPos;
		FLOAT fDistance = D3DXVec3Length(&vecDirection);

		D3DXVec3Normalize(&vecDirection, &vecDirection);

		FLOAT fSpeed = m_fJumpSpeed;
		if (fDistance < METER_TO_DXUNIT(m_fJumpSpeed * m_fHangTime))
		{
			fSpeed = sqrtf(DXUNIT_TO_METER(fDistance) * G / 2.0f);
		}

		m_vecSpeed = vecDirection * fSpeed;
	}
	else
	{
		m_vecPos = m_vecJumpPos + METER_TO_DXUNIT(m_vecSpeed * fElapsedTime);
		m_vecPos.y =  5.0f + METER_TO_DXUNIT((2.0f * m_fJumpSpeed - G * fElapsedTime) * fElapsedTime / 2.0f);
	}

	D3DXMatrixTranslation(&m_world, m_vecPos.x, m_vecPos.y, m_vecPos.z);

	m_Position = pWorld->GetArea(m_vecPos);

	m_ThinkingView.SetPos(m_vecPos.x + 7.5f, 20.0f, m_vecPos.z);

	/*
	D3DXVECTOR3 vecCurrentDir;

	D3DXVec3Normalize(&vecCurrentDir, &m_vecSpeed);

	vecDirection -= vecCurrentDir;

	D3DXVec3Normalize(&vecDirection, &vecDirection);

	D3DXVECTOR3 vecAcceleration;

	D3DXVec3Scale(&vecAcceleration, vecDirection, m_fAcceleration);
	*/

	// Move to next

	// Set matrix

	// End:
	// Return location.
	if (m_Position == m_TargetPosition)
	{
		D3DXVECTOR3 vecDef = vecTo - m_vecPos;
		if (D3DXVec3Length(&vecDef) <= 5.0f)
		{
			memset(&m_TargetPosition, 0xFF, sizeof(m_TargetPosition));
			m_Location = m_Distination;
			m_bInMoving = false;
			return true;
		}
	}

	return false;
}

void Miner::AddToGoldCarried(float fTime)
{
	float val = fTime - m_fLastUpdateTime;
	m_fThirst += val * 1.5f;
	m_fGoldCarried += val;
}

void Miner::IncreaseFatigue(void)
{
}

bool Miner::PocketsFull(void)
{
	return m_fGoldCarried >= 5.0f;
}

bool Miner::Thirsty(void)
{
	return m_fThirst >= 10.0f;
}

void Miner::Sleep(void)
{
	m_fThirst = 0;
}

void Miner::DepositGold(void)
{
	m_iMoneyInBank += (int)m_fGoldCarried;
	m_fGoldCarried = 0.0f;
}

void Miner::SetTargetAmount(void)
{
	m_iTargetAmount = m_iMoneyInBank + 10;
}

bool Miner::Reached(void)
{
	return m_iMoneyInBank >= m_iTargetAmount;
}

bool Miner::Drink(float fTime)
{
	float val = fTime - m_fLastUpdateTime;
	m_fThirst -= val * 5.0f;

	if (m_fThirst <= 0.0f)
	{
		m_fThirst = 0.0f;
		if (m_fGoldCarried > 0.0f) m_fGoldCarried--;
		else m_iMoneyInBank--;
		return true;
	}

	return false;
}


DXMesh Miner::s_mesh;

HRESULT Miner::CreateMesh(LPDIRECT3DDEVICE9 pd3ddev9)
{
	HRESULT hr = D3DXCreateSphere(pd3ddev9, 2.5f, 12, 6, &s_mesh.m_pMesh, &s_mesh.m_pAdjacency);

	if (hr == S_OK)
	{
		s_mesh.m_pMesh->OptimizeInplace(
			D3DXMESHOPT_COMPACT | D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_VERTEXCACHE,
			( DWORD* )s_mesh.m_pAdjacency->GetBufferPointer(), NULL, NULL, NULL );
	}

	LPD3DXMESH pNewMesh = NULL;

	// Optimize the mesh for performance
	if( FAILED( hr = s_mesh.m_pMesh->OptimizeInplace(
		D3DXMESHOPT_COMPACT | D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_VERTEXCACHE,
		( DWORD* )s_mesh.m_pAdjacency->GetBufferPointer(), NULL, NULL, NULL ) ) )
	{
		s_mesh.m_pAdjacency->Release();
		return hr;
	}

	// set materials
	s_mesh.m_pMaterials = new D3DXMATERIAL();
	ZeroMemory(s_mesh.m_pMaterials, sizeof(D3DXMATERIAL));
	
	s_mesh.m_pMaterials->MatD3D.Ambient.a = 1.0f;
	s_mesh.m_pMaterials->MatD3D.Ambient.r = 0.75f;
	s_mesh.m_pMaterials->MatD3D.Ambient.g = 0.75f;
	s_mesh.m_pMaterials->MatD3D.Ambient.b = 0.75f;

	s_mesh.m_pMaterials->MatD3D.Diffuse.a = 1.0f;
	s_mesh.m_pMaterials->MatD3D.Diffuse.r = 0.5f;
	s_mesh.m_pMaterials->MatD3D.Diffuse.g = 0.5f;
	s_mesh.m_pMaterials->MatD3D.Diffuse.b = 0.5f;


	ThinkingView::CreateMesh(pd3ddev9);

	return hr;
}